package typedef

import (
	"os"
	"fmt"
//	"time"
	"math/big"
	"crypto/rand"
	"errors"
	"strings"
	"rwtest/iofunc"
)

// Define class GoThread
type GoThread struct {
	cfg *CfgStruct
	stat *Status
	Id int
	PleaseStop bool
	inited bool
	subDir *iofunc.SubDir
	buf []byte
	err error

	// var for func oneTest()
	randPos64 *big.Int
	randPos int
	writeWeight int
	readWeight int
	removeWeight int
	allWeight int
	result string
	results []string
	action string

	// var for func Run()
	lastMaxSize int
}

func (this *GoThread) Init(pcfg *CfgStruct, id int) {
	this.cfg = pcfg
	this.stat = &this.cfg.Stat
	this.Id = id
	this.PleaseStop = false
	this.subDir = new(iofunc.SubDir)
	this.subDir.Name = fmt.Sprintf("%s/sub_dir_%d", this.cfg.FIDirTest.Name(), this.Id)
	this.subDir.Entry = make(map[string]int)
	this.buf = make([]byte, this.cfg.Number["MAX_SIZE"])
	this.inited = true
}

func (this *GoThread) oneTest() {
	this.writeWeight = this.cfg.Number["WRITE_WEIGHT"]
	this.readWeight = this.cfg.Number["READ_WEIGHT"]
	this.removeWeight = this.cfg.Number["REMOVE_WEIGHT"]
	this.allWeight = this.writeWeight + this.readWeight + this.removeWeight

	this.randPos64, _ = rand.Int(rand.Reader, big.NewInt(int64(this.allWeight - 1)))
	this.randPos = int(this.randPos64.Int64())

	if this.randPos < this.writeWeight {
		// do write
		this.action = "WRITE"
		if iofunc.DirEntryCount(this.subDir.Name) <
				this.cfg.Number["MAX_FILE_COUNT"] {
			this.randPos64, _ = rand.Int(rand.Reader,
				big.NewInt(int64(this.cfg.Number["MAX_SIZE"] - 1)))
			this.randPos = int(this.randPos64.Int64()) + 1
			this.err = iofunc.RandWrite(this.subDir, this.buf, this.randPos)
		} else {
			this.err = errors.New("SKIP: skip write")
		}
	} else if this.randPos < (this.writeWeight + this.readWeight) {
		// do read
		this.action = "READ"
		this.err = iofunc.RandRead(this.subDir, this.cfg.Number["MAX_SIZE"])
	} else {
		// do delete
		this.action = "DELETE"
		this.err = iofunc.RandDelete(this.subDir)
	}
	if this.err == nil {
		this.stat.AddOne(this.action, "SUCCESS")
	} else {
		this.result = fmt.Sprintf("%s", this.err)
		this.results = strings.Split(this.result, ":")
		if this.results[0] == "SKIP" {
			this.stat.AddOne(this.action, "SKIP")
		} else {
			this.stat.AddOne(this.action, "FAILED")
		}
	}
}

func (this *GoThread) Run() {
	if this.inited == false {
		fmt.Printf("ERROR: goroutine %d is uninitialed.\n", this.Id)
		return
	}
	if os.Mkdir(this.subDir.Name, 0775) != nil {
		fmt.Printf("ERROR: mkdir %s failed.\n", this.subDir.Name)
		return
	}
	this.cfg.RunningNumLock.Lock()
	this.cfg.RunningNum = this.cfg.RunningNum + 1
	this.cfg.RunningNumLock.Unlock()

	go func() {
		defer func() {
			if os.RemoveAll(this.subDir.Name) != nil {
				fmt.Printf("ERROR: clean dir %s failed.\n", this.subDir.Name)
			}
			this.cfg.RunningNumLock.Lock()
			this.cfg.RunningNum = this.cfg.RunningNum - 1
			this.PleaseStop = false
			this.cfg.RunningNumLock.Unlock()
		} ()

		// Init byte array with random bytes.
		this.lastMaxSize, this.err = rand.Read(this.buf)
		if this.err != nil {
			fmt.Print("ERROR: Generate random bytes error ")
			fmt.Printf("on thread %d\n", this.Id)
			return
		}

		for ; this.PleaseStop != true; {
			// Reinit byte array when MAX_SIZE changed
			if this.lastMaxSize != this.cfg.Number["MAX_SIZE"] &&
					this.cfg.Number["MAX_SIZE"] != 0 {
				this.buf = make([]byte, this.cfg.Number["MAX_SIZE"])
				this.lastMaxSize, this.err = rand.Read(this.buf)
				if this.err != nil {
					fmt.Print("ERROR: Generate random bytes error ")
					fmt.Printf("on thread %d\n", this.Id)
					return
				}
			}
			this.oneTest()
		}
	} ()
}

